/**
*  Copyright (c) 2011, Alex Theodoridis
*  All rights reserved.

*  Redistribution and use in source and binary forms, with
*  or without modification, are permitted provided that the
*  following conditions are met:
*  Redistributions of source code must retain the above
*  copyright notice, this list of conditions and the following disclaimer.
*  Redistributions in binary form must reproduce the above
*  copyright notice, this list of conditions and the following
*  disclaimer in the documentation and/or other materials
*  provided with the distribution.

*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
*  AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
*  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
*  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
*  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
*  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
*  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
*  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE,
*  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*/

 /** Example
  class State2;
  class State1;

  struct finished{};
  struct repeat{};
  struct working{};

  typedef utils::Entry<State1, finished, State2> Transition1;
  typedef utils::Entry<State2, finished, State1> Transition2;
  typedef utils::Entry<State1, repeat, State1> Transition3;
  typedef utils::Entry<State2, working, State2> Transition4;
  typedef TYPELIST_4( Transition1, Transition2, Transition3, Transition4 ) TransitionList;
  typedef utils::StateMachine<utils::Transitions< TransitionList >, bool, true > StateMachine;
 */
 
/*
 @startuml image.png
 
 
 package "Users" #DDDDDD {
  class State1
  class State2
  class Event
 }
 
 package "Library" #DDDDDD {
  class StateMachine< Transitions >
  interface IState
  Transitions "1" *-- "many" Transition : contains
  class StateHolder<Transitions>
  class Executor< Transitions >
  class Transition< State1, Event, State2 >
  Transition ..> Event
  Transition ..> State1
  Transition ..> State2
  
  StateMachine -|> Executor
  Executor *- StateHolder
  StateHolder *- IState
  StateHolder ..> Transitions
  
 }

 State1 .|> IState
 State2 .|> IState
 State1 ..> Event
 State2 ..> Event
 
 @enduml 
*/

#ifndef STATEMACHINE_H
#define STATEMACHINE_H
#include <memory>
#include <dpsm/TypeList.h>

namespace dpsm
{

namespace priv
{

template<typename State>
class StateHolder;

template<typename RetType,typename Transitions>
class StateMachine;


/// @brief the state interface.
template<typename RetType, typename TransitionsType>
class IState
{
public:
  typedef TransitionsType Transitions;
  typedef StateHolder< IState<RetType, Transitions> > StateHolderType;
  
  RetType ExecuteStep ( StateHolderType& stateHolder ) {
      return ExecuteStepImpl ( stateHolder );
  }

  virtual ~IState() {}

private:
    StateHolderType& m_owner;
    virtual RetType ExecuteStepImpl ( StateHolderType& stateHolder ) = 0;

protected:
    IState ( StateHolderType& stateHolder ) : m_owner ( stateHolder ) {};
};


/// @brief the state holder class which keeps the current state of the state machine.
template< typename State >
class StateHolder
{
private:
    template<class, class, bool> friend class Executor;
    typedef typename State::Transitions Transitions;
    std::auto_ptr< State > m_currentState;

public:
    template< typename StateCreator >
    StateHolder( StateCreator creator){
      m_currentState.reset( creator( *this ) );
    }

    ~StateHolder() {}

    /// @brief will set a new state to the state machine.
    /// @param newState instance of the new state.
    /// @return bool true is the given state is not NULL, false otherwise.
    template< class EventType, class StateType>
    void SendEvent (StateType* state) {
        typedef typename Transitions::template NextState<StateType, EventType>::Result NextState;
        m_currentState.reset ( new NextState(*this) );
    }
    
    /// @brief will set a new state to the state machine.
    /// @param newState instance of the new state.
    /// @return bool true is the given state is not NULL, false otherwise.
    template< class EventType, class StateType, typename Arg1>
    void SendEvent (StateType* state, Arg1 arg1 ) {
        typedef typename Transitions::template NextState<StateType, EventType>::Result NextState;
        m_currentState.reset ( new NextState ( *this, arg1 ) );
    }
    
    /// @brief will set a new state to the state machine.
    /// @param newState instance of the new state.
    /// @return bool true is the given state is not NULL, false otherwise.
    template< typename EventType, class StateType, typename Arg1, typename Arg2>
    void SendEvent (StateType* state, Arg1 arg1, Arg2 arg2) {
        typedef typename Transitions::template NextState<StateType, EventType>::Result NextState;
        m_currentState.reset ( new NextState(*this, arg1, arg2) );
    }
};

/// Executors------------------------------------------------------------
template<typename ReturnType, typename StateHolder, bool includeExecutor>
class Executor {};

template<typename ReturnType, typename StateHolder>
class Executor<ReturnType, StateHolder, false >
{
protected:
    StateHolder m_stateHolder;

protected:
    ~Executor() {}

public:
    template< typename StateCreator>
    Executor( StateCreator creator) : m_stateHolder( creator) {
    }
};

template<typename ReturnType, typename StateHolder>
class Executor<ReturnType, StateHolder, true> : public Executor<ReturnType, StateHolder, false>
{
protected:
    typedef Executor<ReturnType, StateHolder, false> NullExecutor;

protected:
    ~Executor() {}

public:
    template< typename StateCreator >
    Executor( StateCreator creator) : NullExecutor( creator) {}

    ReturnType ExecuteStep() {
      return Executor<ReturnType, StateHolder, false>::m_stateHolder.m_currentState->ExecuteStep ( NullExecutor::m_stateHolder );
    }
};
/// ---------------------------------------------------------------------


/// TypeTraits to find a nexst state-------------------------------------
template<class TList, class CurState, class EventType>
struct FindNextStep;

template<class TList, class CurState, class EventType>
struct FindNextStep
{
  typedef typename FindNextStep<typename TList::Tail, CurState, EventType>::Result Result;
};

template<class TList>
struct FindNextStep<TList, typename TList::Head::CurrentState, typename TList::Head::Event>
{
  typedef typename TList::Head::Result Result;
};

template<class CurState, class EventType>
struct FindNextStep<NullType, CurState, EventType>
{
  typedef NullType Result;
};
/// ---------------------------------------------------------------------

} /// namespace priv

/// @brief Generic state machine implementation.
template<class Transitions, typename RetType = void, bool includeExecutor = false>
class StateMachine : public priv::Executor< RetType, priv::StateHolder< priv::IState<RetType, Transitions> >, includeExecutor >
{
public:
    /// @brief the type of the state.
    typedef priv::IState<RetType, Transitions> State;
    typedef priv::StateHolder<State> StateHolder;
    typedef RetType ReturnType;
    
private:
    typedef priv::Executor<RetType, priv::StateHolder<State>, includeExecutor > Executor;

public:
    /// @brief Constructor will initialize the object.
    /// @param creator the creator function for the first state.
    template< typename StateCreator>
    StateMachine(StateCreator creator) : Executor(creator) {}
    ~StateMachine() { }
};

template<typename CurState, typename EventType, typename NextStateType>
struct Transition
{
  typedef CurState CurrentState;
  typedef NextStateType Result;
  typedef EventType Event;
};

template<class ListType>
struct Transitions 
{ 
  template< typename CurState, typename EventType>
  struct NextState
  {
    typedef typename priv::FindNextStep<ListType, CurState, EventType>::Result Result;
  };
};

}



 
#endif // STATEMACHINE_H


